package zero.lex;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 词法分析
 * 
 * @author Zero
 *
 */
public class Lex {
    private static Lex               lex        = null;
    private static final String      WORDS_FILE = "words.txt";
    private HashMap<String, Integer> wordsIdMap = new HashMap<>();

    private Lex() {

    }

    public static Lex getInstance() {
        if (lex == null) {
            lex = new Lex();
        }
        return lex;
    }

    public void init() {
        try (BufferedReader br = new BufferedReader(new FileReader(WORDS_FILE));) {
            String line;

            while ((line = br.readLine()) != null) {
                if (line.trim().equals("")) {
                    continue;
                }

                String[] tmp   = line.split("\\s+");
                String   words = tmp[0];
                Integer  id    = Integer.parseInt(tmp[1]);
                wordsIdMap.put(words, id);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public List<Token> analyze(File srcFile) {
        List<Token> tokenList = new ArrayList<>();

        if (!srcFile.isFile()) {
            System.err.println("Source file is invalid!");
            return null;
        }

        try (BufferedReader bf = new BufferedReader(new FileReader(srcFile))) {
            int    lineNo = 1;
            String line;

            while ((line = bf.readLine()) != null) {
                tokenScan(tokenList, line, lineNo++);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return tokenList;
    }

    private void tokenScan(List<Token> tokenList, String line, int lineNo) {
        int    start   = 0;
        int    forword = 0;

        char[] buf     = line.toCharArray();

        int    i       = 1;

        while (forword < buf.length) {
            while (start < buf.length && (buf[start] == ' ' || buf[start] == '\t')) {
                ++start;
            }

            if (start >= buf.length || buf[start] == '\n' || buf[start] == '\r') {
                return;
            }

            forword = start;
            if (Character.isLetter(buf[start])) {
                ++forword;

                while (forword < buf.length && Character.isLetterOrDigit(buf[forword])) {
                    ++forword;
                }

                String words = copyToken(buf, start, forword);
                start = forword;

                Token token = new Token();
                token.setContent(words);
                token.setLineNo(lineNo);
                token.setColumnNo(i++);

                if (wordsIdMap.containsKey(words)) {
                    token.setId(wordsIdMap.get(words));
                } else {
                    token.setId(wordsIdMap.get("标识符"));
                }

                tokenList.add(token);
            } else if (Character.isDigit(buf[start])) {
                ++forword;
                while (forword < buf.length && Character.isDigit(buf[forword])) {
                    ++forword;
                }

                String words = copyToken(buf, start, forword);

                Token  token = new Token(wordsIdMap.get("常数"), words, lineNo, ++i);
                tokenList.add(token);

                start = forword;
            } else {
                String word;
                Token  token;

                switch (buf[start]) {
                case '+':
                    ++forword;
                    if (forword < buf.length && buf[forword] == '+') {
                        // 判断是否为 ++
                        ++forword;
                        word  = copyToken(buf, start, forword);
                        token = new Token(wordsIdMap.get(word), word, lineNo, ++i);
                        tokenList.add(token);
                    } else {
                        if (Character.isDigit(buf[forword])) {
                            // 如果是加了符号的数
                            ++forword;
                            word  = copyToken(buf, start, forword);
                            token = new Token(wordsIdMap.get("constant"), word, lineNo, ++i);
                            tokenList.add(token);
                        } else {
                            word  = copyToken(buf, start, forword);
                            token = new Token(wordsIdMap.get(word), word, lineNo, ++i);
                            tokenList.add(token);
                        }
                    }
                    start = forword;
                    break;
                case '-':
                    ++forword;
                    if (forword < buf.length && buf[forword] == '-') {
                        // 判断是否为 --
                        ++forword;
                        word  = copyToken(buf, start, forword);
                        token = new Token(wordsIdMap.get(word), word, lineNo, ++i);
                        tokenList.add(token);
                    } else {
                        if (Character.isDigit(buf[forword])) {
                            // 如果是加了符号的数
                            ++forword;
                            word  = copyToken(buf, start, forword);
                            token = new Token(wordsIdMap.get("constant"), word, lineNo, ++i);
                            tokenList.add(token);
                        } else {
                            word  = copyToken(buf, start, forword);
                            token = new Token(wordsIdMap.get(word), word, lineNo, ++i);
                            tokenList.add(token);
                        }
                    }
                    start = forword;
                    break;
                case '*':
                case '/':
                case '%':
                    ++forword;
                    word = copyToken(buf, start, forword);
                    token = new Token(wordsIdMap.get(word), word, lineNo, ++i);
                    tokenList.add(token);
                    start = forword;
                    break;
                case '<':
                case '>':
                case '!':
                case '=':
                    ++forword;
                    if (forword < buf.length && buf[forword] == '=') {
                        // 如果是 <= >= != ==
                        ++forword;
                    }

                    word = copyToken(buf, start, forword);

                    token = new Token(wordsIdMap.get(word), word, lineNo, ++i);

                    tokenList.add(token);
                    start = forword;
                    break;
                case '{':
                case '}':
                case '(':
                case ')':
                case ',':
                case ';':
                    ++forword;
                    word = copyToken(buf, start, forword);
                    token = new Token(wordsIdMap.get(word), word, lineNo, ++i);
                    tokenList.add(token);
                    start = forword;
                    break;
                default:
                    System.err.println("[line " + lineNo + " column " + i + "]" + "invalid '" + buf[start] + "'");
                    return;
                }
            }
        }
    }

    private String copyToken(char[] buf, int start, int forword) {
        String s = "";
        while (start != forword) {
            s += buf[start];
            ++start;
        }

        return s;
    }

    public Integer getId(String word) {
        return wordsIdMap.get(word);
    }
}
